#include "epd_ses_266.h"

uint8  part_flag=0;
const unsigned char CODE lut_20_vcom0_full[] =
    {
        0x00, 0x08, 0x00, 0x00, 0x00, 0x02,
        0x60, 0x28, 0x28, 0x00, 0x00, 0x01,
        0x00, 0x14, 0x00, 0x00, 0x00, 0x01,
        0x00, 0x12, 0x12, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00};

const unsigned char CODE  lut_21_ww_full[] =
    {
        0x40, 0x08, 0x00, 0x00, 0x00, 0x02,
        0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
        0x40, 0x14, 0x00, 0x00, 0x00, 0x01,
        0xA0, 0x12, 0x12, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const unsigned char CODE  lut_22_bw_full[] =
    {
        0x40, 0x08, 0x00, 0x00, 0x00, 0x02,
        0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
        0x40, 0x14, 0x00, 0x00, 0x00, 0x01,
        0xA0, 0x12, 0x12, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const unsigned char CODE  lut_23_wb_full[] =
    {
        0x80, 0x08, 0x00, 0x00, 0x00, 0x02,
        0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
        0x80, 0x14, 0x00, 0x00, 0x00, 0x01,
        0x50, 0x12, 0x12, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const unsigned char CODE  lut_24_bb_full[] =
    {
        0x80, 0x08, 0x00, 0x00, 0x00, 0x02,
        0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
        0x80, 0x14, 0x00, 0x00, 0x00, 0x01,
        0x50, 0x12, 0x12, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

/******************************partial screen update LUT*********************************/
const unsigned char CODE lut_20_vcom0_partial[] =
    {
        0x00,
        0x19,
        0x01,
        0x00,
        0x00,
        0x01,

};

const unsigned char CODE  lut_21_ww_partial[] =
    {
        // 10 w
        0x00,
        0x19,
        0x01,
        0x00,
        0x00,
        0x01,

};

const unsigned char CODE  lut_22_bw_partial[] =
    {
        // 10 w
        0x80,
        0x19,
        0x01,
        0x00,
        0x00,
        0x01,

};

const unsigned char CODE  lut_23_wb_partial[] =
    {
        // 01 b
        0x40,
        0x19,
        0x01,
        0x00,
        0x00,
        0x01,

};

const unsigned char CODE  lut_24_bb_partial[] =
    {
        // 01 b
        0x00,
        0x19,
        0x01,
        0x00,
        0x00,
        0x01,

};

void DelayMS(uint msec)
{ 
    uint i,j;
    
    for (i=0; i<msec; i++)
        for (j=0; j<535; j++);
}

void SendByte_softSPI(uint8 sdbyte)
{
    uint8 i;
    for (i = 0; i < 8; i++)
    {
        EPD_SCLK_L;
        if (sdbyte & 0x80)
        {
            EPD_SDI_H;
        }
        else
        {
            EPD_SDI_L;
        }
        sdbyte <<= 1;
        EPD_SCLK_H;
    }
    EPD_SCLK_L;
}


void EPD_2IN66_SendCommand(uint8 cmd)
{
    EPD_DC_L;
    EPD_CS_L;
    SendByte_softSPI(cmd);
    EPD_CS_H;
    EPD_DC_H;
}

void EPD_2IN66_SendData(uint8 data)
{
    EPD_DC_H;
    EPD_CS_L;
    SendByte_softSPI(data);
    EPD_CS_H;
}
void EPD_2IN66_SendData_Multi(const uint8 *data, uint16 data_size)
{
//    EPD_DC_H;
//    EPD_CS_L;
    while (data_size)
    {
        EPD_2IN66_SendData((*data++));
        data_size--;
    }
//    EPD_CS_H;
}

void _writeDataPGM(const uint8 *data, uint16 n, uint16 fill_with_zeroes)
{

    for (uint16_t i = 0; i < n; i++)
    {
        EPD_2IN66_SendData(*data++);
    }
    while (fill_with_zeroes > 0)
    {
        EPD_2IN66_SendData(0x00);
        fill_with_zeroes--;
    }
}

void EPD_2IN66_ReadBusy(void)
{
  while(!EPD_BUSY){
  }
}


void EPD_2IN66_Reset(void)
{

    EPD_RST_L;
    DelayMS(10);
    EPD_RST_H;
    DelayMS(10);
}

void EPD_2IN66_TurnOnDisplay(void)
{
    if (part_flag)
    {
        EPD_2IN66_SendCommand(0x92);
    }
    EPD_2IN66_SendCommand(0x12);
    EPD_2IN66_ReadBusy();
}

void EPD_2IN66_TurnOnDisplayEX(void)
{
    if (part_flag)
    {
        EPD_2IN66_SendCommand(0x92);
    }
    EPD_2IN66_SendCommand(0x12);
}

void _InitDisplay()
{
    EPD_2IN66_Reset();
    EPD_2IN66_ReadBusy();

    EPD_2IN66_SendCommand(0x01); // POWER SETTING
    EPD_2IN66_SendData(0x03);
    EPD_2IN66_SendData(0x00);
    EPD_2IN66_SendData(0x2b);
    EPD_2IN66_SendData(0x2b);
    EPD_2IN66_SendData(0x03);
    EPD_2IN66_SendCommand(0x06); // boost soft start
    EPD_2IN66_SendData(0x17);    // A
    EPD_2IN66_SendData(0x17);    // B
    EPD_2IN66_SendData(0x17);    // C

    EPD_2IN66_SendCommand(0x00); // panel setting
    EPD_2IN66_SendData(0xbf&(~(1<<3)));    // LUT from REG 128x296  
    // EPD_2IN66_SendData(0x8b);    // LUT from REG 128x296  
    EPD_2IN66_SendData(0x0d);    // VCOM to 0V fast
    EPD_2IN66_SendCommand(0x30); // PLL setting
    EPD_2IN66_SendData(0x3a);    // 3a 100HZ   29 150Hz 39 200HZ 31 171HZ
    EPD_2IN66_SendCommand(0x61); // resolution setting
    EPD_2IN66_SendData(EPD_2IN66_WIDTH);
    EPD_2IN66_SendData(EPD_2IN66_HEIGHT / 256);
    EPD_2IN66_SendData(EPD_2IN66_HEIGHT % 256);
    EPD_2IN66_SendCommand(0x82); // vcom_DC setting
    // EPD_2IN66_SendData (0x00);   // -0.1
    // EPD_2IN66_SendData (0x08);   // -0.1 + 8 * -0.05 = -0.5V from demo
    //    EPD_2IN66_SendData (0x12);   // -0.1 + 18 * -0.05 = -1.0V from OTP, slightly better
    EPD_2IN66_SendData(0x1c);    // -0.1 + 28 * -0.05 = -1.5V test, better
                                 //    EPD_2IN66_SendData (0x26);   // -0.1 + 38 * -0.05 = -2.0V test, same
                                 // EPD_2IN66_SendData (0x30);   // -0.1 + 48 * -0.05 = -2.5V test, darker
    // EPD_2IN66_SendCommand(0xe0); // resolution setting
    // EPD_2IN66_SendData(0x02);
    // EPD_2IN66_SendCommand(0xe5); // resolution setting
    // EPD_2IN66_SendData(0x32);
    
    EPD_2IN66_SendCommand(0x50); // VCOM AND DATA INTERVAL SETTING
    EPD_2IN66_SendData(0x17);    // WBmode:VBDF 17|D7 VBDW 97 VBDB 57   WBRmode:VBDF F7 VBDW 77 VBDB 37  VBDR B7
}

static void EPD_2IN66_SetLUA(const uint8_t *data, uint16_t n)
{
    uint16_t count;
    for (count = 0; count < n; count++)
    {
        EPD_2IN66_SendData(*(data + count));
    }
}
void EPD_2IN66_Init(void)
{
    part_flag = 0;
    _InitDisplay();
    EPD_2IN66_SendCommand(0x20);
    EPD_2IN66_SetLUA((const uint8 *)lut_20_vcom0_full, sizeof(lut_20_vcom0_full));
    EPD_2IN66_SendCommand(0x21);
    EPD_2IN66_SetLUA((const uint8 *)lut_21_ww_full, sizeof(lut_21_ww_full));
    EPD_2IN66_SendCommand(0x22);
    EPD_2IN66_SetLUA((const uint8 *)lut_22_bw_full, sizeof(lut_22_bw_full));
    EPD_2IN66_SendCommand(0x23);
    EPD_2IN66_SetLUA((const uint8 *)lut_23_wb_full, sizeof(lut_23_wb_full));
    EPD_2IN66_SendCommand(0x24);
    EPD_2IN66_SetLUA((const uint8 *)lut_24_bb_full, sizeof(lut_24_bb_full));
    EPD_2IN66_SendCommand(0x04); // POWER ON
    EPD_2IN66_ReadBusy();
}
void EPD_2IN66_Init_Partial(void)
{
    part_flag = 1;
    _InitDisplay();
    EPD_2IN66_SendCommand(0x20);
    _writeDataPGM((const uint8 *)lut_20_vcom0_partial, sizeof(lut_20_vcom0_partial), 44 - sizeof(lut_20_vcom0_partial));
    EPD_2IN66_SendCommand(0x21);
    _writeDataPGM((const uint8 *)lut_21_ww_partial, sizeof(lut_21_ww_partial), 42 - sizeof(lut_21_ww_partial));
    EPD_2IN66_SendCommand(0x22);
    _writeDataPGM((const uint8 *)lut_22_bw_partial, sizeof(lut_22_bw_partial), 42 - sizeof(lut_22_bw_partial));
    EPD_2IN66_SendCommand(0x23);
    _writeDataPGM((const uint8 *)lut_23_wb_partial, sizeof(lut_23_wb_partial), 42 - sizeof(lut_23_wb_partial));
    EPD_2IN66_SendCommand(0x24);
    _writeDataPGM((const uint8 *)lut_24_bb_partial, sizeof(lut_24_bb_partial), 42 - sizeof(lut_24_bb_partial));
    EPD_2IN66_SendCommand(0x04); // POWER ON
    EPD_2IN66_ReadBusy();
    EPD_2IN66_SendCommand(0x91); // This command makes the display enter partial mode
    EPD_2IN66_SendCommand(0x90); // resolution setting
    EPD_2IN66_SendData(0);       // x-start
    EPD_2IN66_SendData(EPD_2IN66_WIDTH - 1); // x-end

    EPD_2IN66_SendData(0);
    EPD_2IN66_SendData(0); // y-start

    EPD_2IN66_SendData(EPD_2IN66_HEIGHT / 256);
    EPD_2IN66_SendData(EPD_2IN66_HEIGHT % 256 - 1); // y-end
    EPD_2IN66_SendData(0x28);
}

void EPD_2IN66_Clear(void)
{
    unsigned int i;
    // Write Data
    EPD_2IN66_SendCommand(0x10); // Transfer old data
    for (i = 0; i < (EPD_2IN66_WIDTH / 8) * EPD_2IN66_HEIGHT; i++)
    {
        EPD_2IN66_SendData(0x00);
    }
    EPD_2IN66_SendCommand(0x13); // Transfer new data
    for (i = 0; i < (EPD_2IN66_WIDTH / 8) * EPD_2IN66_HEIGHT; i++)
    {
        EPD_2IN66_SendData(0xFF); // Transfer the actual displayed data
    }

    // Refresh
    EPD_2IN66_TurnOnDisplay();
}
/*
 * Image 为NULL，发送0xFF  isold 1发送OLD RAM 0发送NEW RAM
 */
void EPD_2IN66_part_Display(const uint8 *Image, uint8_t isold)
{
    uint16 Width, Height;
    Width = (EPD_2IN66_WIDTH % 8 == 0) ? (EPD_2IN66_WIDTH / 8) : (EPD_2IN66_WIDTH / 8 + 1);
    Height = EPD_2IN66_HEIGHT;
    volatile uint16 Addr = 0;  
    

    EPD_2IN66_SendCommand(isold == 1 ? 0x10 : 0x13);

    if (Image != NULL)
    {
        EPD_2IN66_SendData_Multi(Image, Height * Width);
    }
    else
    {
        
        for (int j = 0; j < Height; j++)
        {
          
            for (int i = 0; i < Width; i++)
            {
                Addr = i + j * Width;
                EPD_2IN66_SendData(0xFF);
            }
        }
    }
}

/******************************************************************************
 function :	Sends the image buffer in RAM to e-Paper and displays
 parameter:
 ******************************************************************************/
void EPD_2IN66_Display(uint8 *Image)
{
    EPD_2IN66_part_Display(NULL, 1);
    EPD_2IN66_part_Display(Image, 0);
    EPD_2IN66_SendCommand(0x11);
}


/******************************************************************************
 function :	Enter sleep mode
 parameter:
 ******************************************************************************/
void EPD_2IN66_Sleep(void)
{
    EPD_2IN66_SendCommand(0X50);
    EPD_2IN66_SendData(0xf7);
    EPD_2IN66_SendCommand(0X02);
    EPD_2IN66_ReadBusy();
    EPD_2IN66_SendCommand(0x07);
    EPD_2IN66_SendData(0xA5);
}
/**
 * @brief  向EPD控制器发送指定大小的显示数据。
 * @param  data 要发送数据的指针。
 * @param  data_size 要发送数据的大小。
 */
void EPD_SendData_Multi(const uint8 *data, uint16 data_size, uint8 Inverse)
{
    EPD_DC_H;
    EPD_CS_L;
    if (data == NULL)
    {
       for (uint i = 0; i < data_size; i++)
        {
            SendByte_softSPI(Inverse == 1 ? 0x00 : 0xff);
        }

    }
    else
    {
        for (uint i = 0; i < data_size; i++)
        {
            SendByte_softSPI(Inverse == 1 ? ~data[i] : data[i]);
        }
    }

    EPD_CS_H;
}
/**
 * @brief  设置EPD显示窗口位置和大小。
 * @param  x 显示窗口起始X位置。
 * @param  y_x8 显示窗口起始Y位置，设置1等于8像素。
 * @param  x_size 显示窗口X方向大小。
 * @param  y_size_x8 显示窗口Y方向大小，设置1等于8像素。
 * @note   指针已被自动设置至窗口的左上角。
 */
void EPD_SetWindow(uint16 x, uint8 y_x8, uint16 x_size, uint8 y_size_x8)
{
   

    
}

/**
 * @brief  绘制UTF8字符串。
 * @param  x 绘制起始X位置。
 * @param  y_x8 绘制起始Y位置，设置1等于8像素。
 * @param  gap 字符间额外间距。
 * @param  str 要绘制的字符串指针。
 * @param  ascii_font ASCII字符字模指针。
 * @param  utf8_font UTF8字符字模指针。
 * @param  ramX 写ram选择 1新 2旧 。
 * @note   调用的文件必须是utf-8编码 否则中文会错误
 */
#define Interval_Ascii_Utf8 0			//ASCII字符与UTF8的间隔
void EPD_DrawFonts(uint16 x, uint8 y_x8, uint8 gap, const char *str, const epdFONT_ascii *ascii_font,
                   const epdFONT_utf8 *utf8_font,uint8 ramX)
{

    uint8 i = 0, utf8_size = 0;
    uint16 x_count = 0, font_size = 0;
    const uint8 *ascii_base_addr = NULL;
    uint32 unicode = 0, unicodeemp = 0;

    x_count = 0;
    while (*str != '\0')
    {
        if ((*str & 0x80) == 0x00) /* 普通ASCII字符 */
        {
            if (ascii_font != NULL)
            {
                font_size = ascii_font->Width * ascii_font->Hight / 8;
                ascii_base_addr = ascii_font->fp + (*str - ascii_font->StartChar) * font_size;
                if ((*str - ascii_font->StartChar) >= 0
                        && ascii_base_addr + font_size <= ascii_font->fp + font_size * ascii_font->num) /* 限制数组范围 */
                {

                    EPD_draw(x + x_count, y_x8, ascii_font->Width, ascii_font->Hight / 8,ascii_base_addr,ramX);
                    // EPD_SetWindow(x + x_count, y_x8, ascii_font->Width, ascii_font->Hight / 8);
                    // EPD_SendRAM(ascii_base_addr, font_size);
                }
                else
                {
                    EPD_draw(x + x_count, y_x8, ascii_font->Width, ascii_font->Hight / 8,NULL,ramX);
                    // font_size = (ascii_font->Width) * (ascii_font->Hight / 8);
                    // EPD_SetWindow(x + x_count, y_x8, ascii_font->Width, ascii_font->Hight / 8);
                    // for (i = 0; i < font_size; i++)
                    // {
                    //     utf8_size = 0xFF; /* 借用变量 */
                    //     EPD_SendRAM(&utf8_size, 1);
                    // }
                }
                x_count += ascii_font->Width + gap;
            }
            else if (*str == ' ' && utf8_font != NULL) /* 未指定ASCII字体时空格为UTF8字体宽度除2 */
            {
                EPD_draw(x + x_count, y_x8, ascii_font->Width, ascii_font->Hight / 8,NULL,ramX);
                // font_size = (utf8_font->Width / 2) * (utf8_font->Hight / 8);
                // EPD_SetWindow(x + x_count, y_x8, utf8_font->Width / 2, utf8_font->Hight / 8);
                // for (i = 0; i < font_size; i++)
                // {
                //     utf8_size = 0xFF; /* 借用变量 */
                //     EPD_SendRAM(&utf8_size, 1);
                // }
                x_count += utf8_font->Width / 2 + gap;
            }
        }
        else if (utf8_font != NULL) /* UTF8字符 */
        {
            unicode = 0x000000;
            utf8_size = 0;
            for (i = 0; i < 5; i++)
            {
                if (*str & (0x80 >> i))
                {
                    utf8_size += 1;
                }
                else
                {
                    break;
                }
            }
            switch (utf8_size)
            {
            case 2:
                if (*(str + 1) != '\0')
                {
                    unicode = ((uint32) (*str & 0x1F)) << 6;
                    str += 1;
                    unicode |= (uint32) *str & 0x3F;
                }
                break;
            case 3:
                if (*(str + 1) != '\0' && *(str + 2) != '\0')
                {
                    unicode = ((uint32) (*str & 0x0F)) << 12;
                    str += 1;
                    unicode |= ((uint32) (*str & 0x3F)) << 6;
                    str += 1;
                    unicode |= (uint32) *str & 0x3F;
                }
                break;
            case 4:
                if (*(str + 1) != '\0' && *(str + 2) != '\0' && *(str + 3) != '\0')
                {
                    unicode = ((uint32) (*str & 0x07)) << 18;
                    str += 1;
                    unicode |= ((uint32) (*str & 0x3F)) << 12;
                    str += 1;
                    unicode |= ((uint32) (*str & 0x3F)) << 6;
                    str += 1;
                    unicode |= (uint32) *str & 0x3F;
                }
                break;
            }
            if (unicode != 0)
            {
                font_size = utf8_font->Width * utf8_font->Hight / 8;
                for (i = 0; i < utf8_font->num; i++) /* 限制数组范围 */
                {
                    unicodeemp = (uint32) utf8_font->fp[0 + (font_size + 3) * i] << 16;
                    unicodeemp |= (uint32) utf8_font->fp[1 + (font_size + 3) * i] << 8;
                    unicodeemp |= (uint32) utf8_font->fp[2 + (font_size + 3) * i];
                    if (unicodeemp == unicode)
                    {
                        EPD_draw(x + x_count + Interval_Ascii_Utf8, y_x8, utf8_font->Width, utf8_font->Hight / 8,utf8_font->fp + 3 + (font_size + 3) * i,ramX);
                   
                        // EPD_SetWindow(x + x_count + Interval_Ascii_Utf8, y_x8, utf8_font->Width, utf8_font->Hight / 8);
                        // EPD_SendRAM(utf8_font->fp + 3 + (font_size + 3) * i, font_size);
                        break;
                    }
                }
            }
            x_count += utf8_font->Width + gap;
        }
        str += 1;
    }
}

void EPD_draw(uint16 x, uint8 y_x8, uint16 x_size, uint8 y_size_x8, const uint8 *dat, uint8 ramX)
{
    uint16 data_size = x_size * y_size_x8;
    EPD_2IN66_SendCommand(0x90);

    EPD_2IN66_SendData(y_x8 << 3);
    EPD_2IN66_SendData(((y_x8 + y_size_x8) << 3) - 1);
    EPD_2IN66_SendData((x >> 8) & 0xff);
    EPD_2IN66_SendData(x & 0xff);
    EPD_2IN66_SendData(((x + x_size) >> 8) & 0xff);
    EPD_2IN66_SendData((x + x_size) & 0xff);

    if (dat == NULL)
    {
        if (ramX & 1)
        {
            EPD_2IN66_SendCommand(0x10);

            EPD_SendData_Multi(dat, data_size, 1);
        }
        if (ramX & 2)
        {
            EPD_2IN66_SendCommand(0x13);
            EPD_SendData_Multi(dat, data_size, 0);
        }
    }
    else
    {
        if (ramX & 1)
        {
            EPD_2IN66_SendCommand(0x10);
            EPD_SendData_Multi(dat, data_size, 1);

        }
        if (ramX & 2)
        {
            EPD_2IN66_SendCommand(0x13);
            EPD_SendData_Multi(dat, data_size,0);
        }
    }
}

const uint8_t CODE *Battery_ICON[] = {Bitmap_bat0, Bitmap_bat20, Bitmap_bat40, Bitmap_bat60, Bitmap_bat80, Bitmap_bat100};

void Draw_Battery(uint16_t x, uint8_t y, uint16_t max_voltage, uint16_t min_voltage, uint16_t voltage)
{

    const uint8_t *  battery = NULL;
    uint16_t voltage_size =0;

    if (voltage <= min_voltage)
    {
        battery =(const uint8_t *) Bitmap_bat0;
        goto END;
    }

    if (voltage >= max_voltage)
    {
        battery = (const uint8_t *)Bitmap_bat100;
        goto END;
    }
    voltage_size = (uint16_t)(max_voltage - min_voltage);

    for (int i = 1; i < sizeof(Battery_ICON); i++)
    {

        if ((voltage_size * i / 5 + min_voltage) >= voltage)
        {
            battery = (const uint8_t *)Battery_ICON[i];
            break;
        }
    }

END:
    EPD_draw(x, y, 27, 2, battery,3);
}